package pt.inescid.components.policyengine.heimdall.pap.base;

import pt.inescid.components.policyengine.heimdall.commons.TimeUtilities;

public class QuantifierDefinition implements Cloneable {

	public static final String[] QUANTIFIER_TYPE_CODES = {
		"undefined",
		"exists",
		"forAll",
		"atLeast",
		"atMost",
		"exactly"};

	public static final int UNDEFINED_QUANTIFIER = 0;
	public static final int EXISTS_QUANTIFIER = 1;
	public static final int FOR_ALL_QUANTIFIER = 2;
	public static final int AT_LEAST_QUANTIFIER = 3;
	public static final int AT_MOST_QUANTIFIER = 4;
	public static final int EXACTLY_QUANTIFIER = 5;

	private String name;
	private CesDefinition ces;
	private DomainDefinition domain;
	private int type;
	private int value;
	
	private long withinMilli;
	private RuleDefinition restriction;

	public QuantifierDefinition(String name, 
			String cesName, 
			String domainName, 
			String typeCode, 
			String valueAsString,
			String withinMilli,
			ModelDefinition currentModelDef) throws Exception {
		super();
		this.name = name;
		this.ces = currentModelDef.getCes(cesName);
		this.domain = currentModelDef.getDomain(domainName);;
		
		this.type = UNDEFINED_QUANTIFIER;
		for (int i = 0; i < QUANTIFIER_TYPE_CODES.length; i++) {
			if (typeCode.equals(QUANTIFIER_TYPE_CODES[i])) {
				this.type = i;
			}
		}
		if (this.type == UNDEFINED_QUANTIFIER) {
			throw new Exception("Tipo de quantificador desconhecido");
		}
		this.value = Integer.parseInt(valueAsString);
		
		this.withinMilli = TimeUtilities.parseTimeQty(withinMilli);
		
		if (this.withinMilli == 0) {
			this.restriction = null;
		}
		else {
			// Future Events
			CompositeDefinition currentCondition = null;
			CompositeDefinition nextCondition = null;
			EventFieldDefinition ceOperand = null;
			EventFieldDefinition otherEventOperand = null;

			currentCondition = new CompositeDefinition(
					RuleDefinition.LESSER_THAN_OPERATOR,
					null);
			
			
			nextCondition = new CompositeDefinition(
					RuleDefinition.SUBTRACT_OPERATOR,
					null);
			
			ceOperand = new EventFieldDefinition(
					ces.getModelDef(),
					RuleDefinition.EVENT_FIELD_EXPRESSION,
					RuleDefinition.EXPRESSION_VALUE_CODES[
                    RuleDefinition.TIMESTAMP_VALUE],
                    nextCondition,
			        "ce.executionTime");
			
			otherEventOperand = new EventFieldDefinition(
					ces.getModelDef(),
					RuleDefinition.EVENT_FIELD_EXPRESSION,
					RuleDefinition.EXPRESSION_VALUE_CODES[
                    RuleDefinition.TIMESTAMP_VALUE],
                    nextCondition,
                    this.name + ".executionTime");
			
			
			if (ces.isObligation()) {
				
				// (x.time-ce.time) < within
				nextCondition.addOperand(otherEventOperand);
				nextCondition.addOperand(ceOperand);
			}
			else {
				
				// (ce.time-x.time) < within
				nextCondition.addOperand(ceOperand);
				nextCondition.addOperand(otherEventOperand);
			}
			
			currentCondition.addOperand(nextCondition);
			
			currentCondition.addOperand(new SimpleDefinition(
					RuleDefinition.LITERAL_EXPRESSION,
					RuleDefinition.EXPRESSION_VALUE_CODES[
                    RuleDefinition.TIMESTAMP_VALUE],
                    currentCondition,
                    String.valueOf(this.withinMilli)));
			
			this.restriction = currentCondition;
		}
	}

	public CesDefinition getCes() {
		return ces;
	}

	public DomainDefinition getDomain() {
		return domain;
	}

	public String getName() {
		return name;
	}

	public int getType() {
		return type;
	}

	public int getValue() {
		return value;
	}

	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      isObligation       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public boolean isObligation() {
		return (ces.isObligation());
	} // isObligation
	

	
	
//  ----------------------------------------------------------
//  ------------------------   toString   ------------------------
//  ----------------------------------------------------------
	public String toString() {
		String result = QUANTIFIER_TYPE_CODES[type] + " " + 
			(type >= AT_LEAST_QUANTIFIER ? (value + " ") : "") +
			name + " IN " + domain.getName() + "." + ces.getName(); 
		
		return (result);
	}

//  ----------------------------------------------------------
//  ------------------------   getFunction   ------------------------
//  ----------------------------------------------------------
	public String getFunction() {
		return (toString());
	}

	public RuleDefinition getRestriction() {
		return restriction;
	}

	public long getWithinMilli() {
		return withinMilli;
	}

}; // QuantifierDefinition
